#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#include <errno.h>
#include <signal.h>
#include <time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <mysql/mysql.h>
#include <pthread.h>

// 程序配置
#define PHP_SERVER_PORT 8085      // PHP服务器监听端口
#define SERIAL_DEVICE "/dev/ttyUSB1" // 默认串口设备
#define MYSQL_HOST "127.0.0.1"    // MySQL主机
#define MYSQL_USER "root"         // MySQL用户名
#define MYSQL_PASS "root"     // MySQL密码
#define MYSQL_DB "aajdk"    // MySQL数据库名
#define MYSQL_TABLE "serial_logs" // MySQL表名

volatile sig_atomic_t keep_running = 1;// 全局变量
int serial_fd = -1;
MYSQL *mysql_conn = NULL;
pthread_rwlock_t serial_rwlock = PTHREAD_RWLOCK_INITIALIZER; // 读写锁

void signal_handler(int sig) {// 信号处理函数
    if (sig == SIGINT) {
        printf("\n[INFO] 收到终止信号，正在退出程序...\n");
        keep_running = 0;
    }
}
int configure_serial_port(int fd) {// 配置串口
    struct termios tty;
    if (tcgetattr(fd, &tty) != 0) {
        perror("tcgetattr 失败");
        return -1;
    }
    // 设置波特率为115200
    cfsetispeed(&tty, B115200);
    cfsetospeed(&tty, B115200);
    // 控制模式标志
    tty.c_cflag &= ~PARENB; // 无奇偶校验
    tty.c_cflag &= ~CSTOPB; // 1位停止位
    tty.c_cflag &= ~CSIZE;  // 清除数据位掩码
    tty.c_cflag |= CS8;     // 8位数据位
    tty.c_cflag &= ~CRTSCTS; // 禁用硬件流控制
    tty.c_cflag |= CREAD | CLOCAL; // 启用接收，忽略调制解调器控制线
    // 本地模式标志
    tty.c_lflag &= ~ICANON; // 非规范模式
    tty.c_lflag &= ~ECHO;   // 禁用回显
    tty.c_lflag &= ~ECHOE;  // 禁用擦除
    tty.c_lflag &= ~ECHONL; // 禁用换行回显
    tty.c_lflag &= ~ISIG;   // 禁用信号字符
    // 输入模式标志
    tty.c_iflag &= ~(IXON | IXOFF | IXANY); // 禁用软件流控制
    tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL);
    // 输出模式标志
    tty.c_oflag &= ~OPOST; // 原始输出
    tty.c_oflag &= ~ONLCR; // 禁止将换行符转换为CR/LF
    // 特殊字符
    tty.c_cc[VMIN] = 0;    // 非阻塞模式
    tty.c_cc[VTIME] = 10;  // 超时时间 (1秒 = 10 deciseconds)
    // 应用设置
    if (tcsetattr(fd, TCSANOW, &tty) != 0) {
        perror("tcsetattr 失败");
        return -1;
    }
    // 刷新输入输出缓冲区
    tcflush(fd, TCIOFLUSH);
    return 0;
}
int init_mysql_connection() {// 初始化MySQL连接
    mysql_conn = mysql_init(NULL);
    if (!mysql_conn) {
        fprintf(stderr, "MySQL初始化失败: %s\n", mysql_error(mysql_conn));
        return -1;
    }
    if (!mysql_real_connect(mysql_conn, MYSQL_HOST, MYSQL_USER, MYSQL_PASS,MYSQL_DB, 0, NULL, 0)) {
        fprintf(stderr, "MySQL连接失败: %s\n", mysql_error(mysql_conn));
        mysql_close(mysql_conn);
        return -1;
    }
    printf("[MySQL] 成功连接到数据库\n");
    // 创建表（如果不存在）
    char query[512];
    snprintf(query, sizeof(query),
             "CREATE TABLE IF NOT EXISTS %s ("
             "id INT AUTO_INCREMENT PRIMARY KEY,"
             "timestamp DATETIME NOT NULL,"
             "source VARCHAR(20) NOT NULL,"
             "data VARCHAR(1024) NOT NULL"
             ")", MYSQL_TABLE);
    if (mysql_query(mysql_conn, query)) {
        fprintf(stderr, "创建表失败: %s\n", mysql_error(mysql_conn));
        return -1;
    }
    return 0;
}
void store_to_mysql(const char *source,const char *data, size_t length) {// 将数据存储到MySQL
    if (!mysql_conn) return;
    char query[1024];
    char escaped_data[64];
    // 转义数据防止SQL注入
    mysql_real_escape_string(mysql_conn, escaped_data, data, length > 255 ? 255 : length);
    snprintf(query, sizeof(query),
             "INSERT INTO %s (timestamp, source, data) "
             "VALUES (NOW(), '%s' ,'%s')",MYSQL_TABLE,source, escaped_data);
    printf("insert query %s",query);
    if (mysql_query(mysql_conn, query)) {
        fprintf(stderr, "插入数据失败: %s\n", mysql_error(mysql_conn));
    }
}

void *serial_read_thread(void *arg) {// 串口读取线程
    printf("[串口监控] 线程启动\n");
    char buffer[1024];
    while (keep_running) {
        pthread_rwlock_rdlock(&serial_rwlock);// 获取读锁（允许多个读操作同时进行）
        ssize_t n = read(serial_fd, buffer, sizeof(buffer) - 1);
        pthread_rwlock_unlock(&serial_rwlock);
        if (n > 0) {
            buffer[n] = '\0'; // 确保字符串结束
            printf("[串口接收] %zd 字节: %s\n", n, buffer);           
            store_to_mysql("serial",buffer,n);// 存储到MySQL
        } else if (n < 0 && errno != EAGAIN) {
            perror("读取串口错误");
            break;
        }
        usleep(100000); // 100ms休眠
    }
    printf("[串口监控] 线程退出\n");
    return NULL;
}
void *php_server_thread(void *arg) {// PHP服务器线程
    int server_fd, client_fd;
    struct sockaddr_in server_addr, client_addr;
    socklen_t client_len = sizeof(client_addr);
    if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {// 创建套接字
        perror("套接字创建失败");
        return NULL;
    }
    int reuse = 1;// 设置SO_REUSEADDR选项
    if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse))) {
        perror("设置SO_REUSEADDR失败");
    }
    // 配置服务器地址
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = INADDR_ANY;
    server_addr.sin_port = htons(PHP_SERVER_PORT);
    if (bind(server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr))) {// 绑定套接字
        perror("绑定失败");
        close(server_fd);
        return NULL;
    }
    if (listen(server_fd, 5)) {// 开始监听
        perror("监听失败");
        close(server_fd);
        return NULL;
    }
    printf("[PHP服务] 启动成功，监听端口 %d\n", PHP_SERVER_PORT);
    while (keep_running) {
        // 设置超时以检查终止信号
        fd_set read_fds;
        FD_ZERO(&read_fds);
        FD_SET(server_fd, &read_fds);
        struct timeval timeout;
        timeout.tv_sec = 1;
        timeout.tv_usec = 0;
        int ready = select(server_fd + 1, &read_fds, NULL, NULL, &timeout);
        if (ready == -1) {
            perror("select 错误");
            continue;
        } else if (ready == 0) {
            // 超时，检查是否继续运行
            continue;
        }
        // 接受客户端连接
        client_fd = accept(server_fd, (struct sockaddr *)&client_addr, &client_len);
        if (client_fd < 0) {
            perror("接受连接失败");
            continue;
        }
        printf("[PHP服务] 新连接: %s\n", inet_ntoa(client_addr.sin_addr));
        char buffer[2048];
        ssize_t n = read(client_fd, buffer, sizeof(buffer) - 1);
        if (n > 0) {
            buffer[n] = '\0';
            printf("[PHP接收] %zd 字节: %s\n", n, buffer);
            // 获取写锁（独占访问）
            pthread_rwlock_wrlock(&serial_rwlock);
            ssize_t written = write(serial_fd, buffer, n);
            char end_chars[] = "\r\n";
            ssize_t written_end_char = write(serial_fd,end_chars,sizeof(end_chars)-1);
            pthread_rwlock_unlock(&serial_rwlock);
            if (written > 0) {
                printf("[串口发送] %zd 字节: %s\n", written, buffer);
                store_to_mysql("PHP", buffer, n);// 存储到MySQL
            } else {
                perror("写入串口失败");
            }
            const char *response = "OK";// 发送响应给PHP
            write(client_fd, response, strlen(response));
        }
        close(client_fd);
    }
    close(server_fd);
    printf("[PHP服务] 线程退出\n");
    return NULL;
}
int main(int argc,char **argv) {
    // 注册信号处理
    signal(SIGINT, signal_handler);
    printf("========================================\n");
    printf("串口通信与数据存储系统 (读写锁优化版)\n");
    printf("版本: 2.0\n");
    printf("========================================\n");
    if (init_mysql_connection() != 0) {// 初始化MySQL连接
        fprintf(stderr, "[错误] MySQL初始化失败\n");
        return EXIT_FAILURE;
    }
    serial_fd = open(argv[1], O_RDWR | O_NOCTTY | O_NDELAY);//SERIAL_DEVICE// 打开串口设备
    if (serial_fd == -1) {
        perror("打开串口设备失败");
        return EXIT_FAILURE;
    }
    if (configure_serial_port(serial_fd) != 0) {// 配置串口
        close(serial_fd);
        return EXIT_FAILURE;
    }
    printf("[串口] %s 已打开并配置\n", argv[1]);//SERIAL_DEVICE
    pthread_t serial_thread, php_thread;// 创建线程
    if (pthread_create(&serial_thread, NULL, serial_read_thread, NULL)) {
        perror("创建串口监控线程失败");
        close(serial_fd);
        return EXIT_FAILURE;
    }
    if (pthread_create(&php_thread, NULL, php_server_thread, NULL)) {
        perror("创建PHP服务线程失败");
        keep_running = 0;
        pthread_join(serial_thread, NULL);
        close(serial_fd);
        return EXIT_FAILURE;
    }
    pthread_join(php_thread, NULL);// 等待线程结束
    pthread_join(serial_thread, NULL);
    close(serial_fd);// 清理资源
    if (mysql_conn) {
        mysql_close(mysql_conn);
        printf("[MySQL] 连接已关闭\n");
    }
    pthread_rwlock_destroy(&serial_rwlock);// 销毁读写锁
    printf("[INFO] 程序退出\n");
    return EXIT_SUCCESS;
}

